﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Documents;
using System.Reflection;
using Kokomo.Lexical.Coding;
using Kokomo.Lexical.Expressions;
using System.Windows.Media;
using System.Windows;
using Expression = Kokomo.Lexical.Expressions.Expression;
using Kokomo.PeInspector.Reflection.Disassembly;
using Kokomo.PeInspector.Reflection.Query;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Collections;
using Kokomo.Mirror.CliMetadata;

namespace Kokomo.PeInspector.Reflection
{
	public class CSharpDocumentBuilder : BracedCodeDocumentBuilder
	{
		static string[] _keywords;

		static CSharpDocumentBuilder()
		{
			_keywords = new string[] {
				"abstract", "event", "new", "struct",
				"as", "explicit", "null", "switch",
				"base", "extern", "object", "this",
				"bool", "false", "operator", "throw",
				"break", "finally", "out", "true",
				"byte", "fixed", "override", "try",
				"case", "float", "params", "typeof",
				"catch", "for", "private", "uint",
				"char", "foreach", "protected", "ulong",
				"checked", "goto", "public", "unchecked",
				"class", "if", "readonly", "unsafe",
				"const", "implicit", "ref", "ushort",
				"continue", "in", "return", "using",
				"decimal", "int", "sbyte", "virtual",
				"default", "interface", "sealed", "volatile",
				"delegate", "internal", "short", "void",
				"do", "is", "sizeof", "while",
				"double", "lock", "stackalloc",
				"else", "long", "static",
				"enum", "namespace", "string"
			};

			// This class does hide some details to comply with the C# compiler
			// * Omits System.ParamArrayAttribute for parameter declarations
		}
		static bool IsKeyword(string text)
		{
			if (text == null) throw new ArgumentNullException("text");

			return _keywords.Contains(text);
		}

		public CSharpDocumentBuilder(
			ICodeWriter writer,
			MemberInfo memberContext,
			Func<MethodBase, MethodDecompilation> decompileDelegate,
			string[] importedNamespaces = null
			)
			: base(writer, memberContext, decompileDelegate, importedNamespaces)
		{
			Dictionary<Type, Action<Statement>> statementWriterTable = new Dictionary<Type, Action<Statement>>();
			this.DefineStatementWriters(statementWriterTable);
			this.StatementWriterTable = statementWriterTable;

			this.InitializeExpressionWriterTable();

			this.WriteNamespaceImports();
		}

		protected void WriteNamespaceImports()
		{
			if (
				(this.ImportedNamespaces != null)
				&& (this.ImportedNamespaces.Length > 0)
				)
			{
				foreach (var import in this.ImportedNamespaces)
				{
					this.WriteKeywordText("using");
					this.WriteText(" ");
					this.WriteText(import);
					this.WriteStatementTerminator();
					this.Writer.WriteLineBreak();
				}
				this.Writer.WriteLineBreak();
			}
		}

		public static string DetermineNamespaceContext(MemberInfo member)
		{
			if (member == null) return string.Empty;

			while (member.ReflectedType != null)
			{
				member = member.ReflectedType;
			}

			Type reflectedType = (Type)member;
			return reflectedType.Namespace;
		}

		protected void WriteTypeReference(MemberInfo member, MemberReferenceFlags flags = MemberReferenceFlags.None, Type[] genericArguments = null)
		{
			Type type = member as Type;
			if (type == null) type = member.ReflectedType;

			if (type.IsGenericParameter)
			{
				this.WriteText(MemberViewInfo.GetMemberName(type));
			}
			else if (type.IsByRef)
			{
				this.WriteTypeReference(type.GetElementType(), flags, genericArguments);
				this.Writer.WriteText("&", CodeTextType.Operator);
			}
			else if (type.IsPointer)
			{
				this.WriteTypeReference(type.GetElementType(), flags, genericArguments);
				this.Writer.WriteText("*", CodeTextType.Operator);
			}
			else if (type.IsArray)
			{
				this.WriteTypeReference(type.GetElementType(), flags, genericArguments);
				int rank = type.GetArrayRank();
				if (rank == 1)
					this.WriteText("[]");
				else
				{
					this.WriteText("[");
					this.WriteText(new string(',', rank - 1));
					this.WriteText("]");
				}
			}
			else
			{
				bool isNullable =
					type.IsGenericType
					&& MemberEqualityComparer.Instance.Equals(type.GetGenericTypeDefinition(), typeof(Nullable<>))
					;
				if (isNullable)
				{
					Type valueType = type.GetGenericArguments()[0];
					this.WriteTypeReference(valueType);
					this.WriteText("?");
					return;
				}

				// Check for built-in type
				string keyword = null;
				if (MemberEqualityComparer.Equal(type, typeof(void))) keyword = "void";
				else if (MemberEqualityComparer.Equal(type, typeof(object))) keyword = "object";
				else if (MemberEqualityComparer.Equal(type, typeof(string))) keyword = "string";
				else if (MemberEqualityComparer.Equal(type, typeof(bool))) keyword = "bool";
				else if (MemberEqualityComparer.Equal(type, typeof(byte))) keyword = "byte";
				else if (MemberEqualityComparer.Equal(type, typeof(sbyte))) keyword = "sbyte";
				else if (MemberEqualityComparer.Equal(type, typeof(char))) keyword = "char";
				else if (MemberEqualityComparer.Equal(type, typeof(short))) keyword = "short";
				else if (MemberEqualityComparer.Equal(type, typeof(ushort))) keyword = "ushort";
				else if (MemberEqualityComparer.Equal(type, typeof(int))) keyword = "int";
				else if (MemberEqualityComparer.Equal(type, typeof(uint))) keyword = "uint";
				else if (MemberEqualityComparer.Equal(type, typeof(long))) keyword = "long";
				else if (MemberEqualityComparer.Equal(type, typeof(ulong))) keyword = "ulong";
				else if (MemberEqualityComparer.Equal(type, typeof(double))) keyword = "double";
				else if (MemberEqualityComparer.Equal(type, typeof(float))) keyword = "float";
				if (keyword != null)
				{
					this.Writer.WriteMemberReference(member, keyword, CodeTextType.Keyword);
					return;
				}

				if (type.IsNested && !type.IsGenericParameter)
				{
					bool isLocalType = (type.ReflectedType == this.TypeContext);
					if (!isLocalType)
					{
						Type[] parentGenericArguments = null;
						if (type.IsGenericType)
						{
							genericArguments = type.GetGenericArguments();
							if (type.ReflectedType.IsGenericType)
							{
								parentGenericArguments = genericArguments;
								genericArguments = null;
							}
						}

						this.WriteTypeReference(type.ReflectedType, flags, parentGenericArguments);
						this.WriteText(".");
					}
				}
				else
				{
					bool isLocalType = this.IsLocalType(type);
					if (!isLocalType && (type.Namespace != null))
					{
						this.WriteText(type.Namespace);
						this.WriteText(".");
					}

					if ((genericArguments == null) && type.IsGenericType) genericArguments = type.GetGenericArguments();
				}
				string principalName = type.PrincipalName();
				if (
					flags.HasFlag(MemberReferenceFlags.Attribute)
					&& principalName.EndsWith("Attribute")
					)
				{
					principalName = principalName.Remove(principalName.Length - "Attribute".Length);
				}
				this.Writer.WriteMemberReference(member, principalName, CodeTextType.TypeName);

				if (genericArguments != null)
				{
					this.WriteGenericParameterList(genericArguments, false);
				}
			}
		}
		protected void WriteMemberReference(MemberInfo member, MemberReferenceFlags flags = MemberReferenceFlags.None)
		{
			if (member == null) throw new ArgumentNullException("member");

			Type type = member as Type;
			ConstructorInfo constructor = member as ConstructorInfo;
			if ((member is Type) || (member is ConstructorInfo))
			{
				this.WriteTypeReference(member, flags);
			}
			else
			{
				string text = member.PrincipalName();// this.GetMemberReferenceText(member);
				this.Writer.WriteMemberReference(member, text);

				MethodInfo method = member as MethodInfo;
				Type[] genericArguments = null;
				if ((method != null) && (method.IsGenericMethod)) genericArguments = method.GetGenericArguments();

				if (genericArguments != null) this.WriteGenericParameterList(genericArguments, false);
			}

		}

		public string GetMemberAccessKeyword(int access)
		{
			string accessKeyword = null;
			switch (access)
			{
				case (int)FieldAttributes.Private:
					accessKeyword = "private";
					break;
				case (int)FieldAttributes.Assembly:
					accessKeyword = "internal";
					break;
				case (int)FieldAttributes.Family:
					accessKeyword = "protected";
					break;
				case (int)FieldAttributes.FamORAssem:
					accessKeyword = "protected internal";
					break;
				case (int)FieldAttributes.Public:
					accessKeyword = "public";
					break;
			}

			return accessKeyword;
		}
		public string GetTypeVisibilityKeyword(Type type)
		{
			if (type == null) throw new ArgumentNullException("type");

			string keyword = null;
			switch (type.Attributes & TypeAttributes.VisibilityMask)
			{
				case TypeAttributes.NestedAssembly:
					keyword = "internal";
					break;
				case TypeAttributes.NestedFamANDAssem:
				case TypeAttributes.NestedFamORAssem:
					keyword = "protected internal";
					break;
				case TypeAttributes.NestedFamily:
					keyword = "internal";
					break;
				case TypeAttributes.NestedPrivate:
					keyword = "private";
					break;
				case TypeAttributes.NestedPublic:
				case TypeAttributes.Public:
					keyword = "public";
					break;
			}

			return keyword;
		}

		public string GetVTableKeyword(MethodInfo method)
		{
			if (method == null) throw new ArgumentNullException("method");

			string vtableKeyword = null;

			Type declaringType = method.DeclaringType;
			if (declaringType != null)
			{
				if (declaringType.IsValueType)
				{
					if (method.IsStatic) vtableKeyword = "static";
				}
				else
				{
					if (method.IsAbstract) vtableKeyword = "abstract";
					else if (method.IsStatic) vtableKeyword = "static";
					else if (method.IsVirtual)
					{
						MethodInfo baseMethod = method.GetBaseDefinition();
						if (method != baseMethod)
						{
							vtableKeyword = (method.IsFinal ? "sealed override" : "override");
						}
						else
						{
							vtableKeyword = "virtual";
						}
					}
				}
			}

			return vtableKeyword;
		}

		public string GetFieldSpecifierKeyword(FieldAttributes attributes)
		{
			string keyword = null;

			if (attributes.HasFlag(FieldAttributes.Literal))
			{
				keyword = "const";
			}
			else if (attributes.HasFlag(FieldAttributes.InitOnly))
			{
				keyword = "readonly";
			}

			return keyword;
		}

		public static string EscapeString(string str)
		{
			StringBuilder builder = new StringBuilder(str.Length);
			foreach (var c in str)
			{
				switch (c)
				{
					case '\0':
						builder.Append(@"\0");
						break;
					case '\r':
						builder.Append(@"\r");
						break;
					case '\n':
						builder.Append(@"\n");
						break;
					case '\t':
						builder.Append(@"\t");
						break;
					case '\\':
						builder.Append(@"\\");
						break;
					case '\"':
						builder.Append(@"\""");
						break;
					default:
						builder.Append(c);
						break;
				}
			}

			return builder.ToString();
		}

		public string StatementTerminator { get { return ";"; } }

		//public string GetMemberReferenceText(MemberInfo member)
		//{
		//    return GetMemberReferenceText(member, this.ImportedNamespaces);
		//}
		//public static string GetMemberReferenceText(MemberInfo member, MemberInfo memberContext)
		//{
		//    if (memberContext == null) throw new ArgumentNullException("memberContext");

		//    return GetMemberReferenceText(member, new string[] { DetermineNamespaceContext(memberContext) });
		//}
		//public static string GetMemberReferenceText(MemberInfo member, IEnumerable<string> importedNamespaces = null)
		//{
		//    if (member == null) throw new ArgumentNullException("member");

		//    Type type = member as Type;

		//    if (type == null) return member.PrincipalName();

		//    bool isNullable =
		//        type.IsGenericType
		//        && (type.GetGenericTypeDefinition() == typeof(Nullable<>))
		//        ;
		//    if (isNullable)
		//    {
		//        Type valueType = type.GetGenericArguments()[0];
		//        string valueTypeText = GetMemberReferenceText(valueType, importedNamespaces);
		//        return valueTypeText + "?";
		//    }

		//    // Check for built-in type
		//    if (type == typeof(void)) return "void";
		//    if (type == typeof(object)) return "object";
		//    if (type == typeof(string)) return "string";
		//    if (type == typeof(bool)) return "bool";
		//    if (type == typeof(byte)) return "byte";
		//    if (type == typeof(sbyte)) return "sbyte";
		//    if (type == typeof(char)) return "char";
		//    if (type == typeof(short)) return "short";
		//    if (type == typeof(ushort)) return "ushort";
		//    if (type == typeof(int)) return "int";
		//    if (type == typeof(uint)) return "uint";
		//    if (type == typeof(long)) return "long";
		//    if (type == typeof(ulong)) return "ulong";

		//    bool requireFullTypeName =
		//        (importedNamespaces == null)
		//        || (!importedNamespaces.Contains(type.Namespace))
		//        ;

		//    string typeName = requireFullTypeName
		//        ? type.QualifiedFullName()
		//        : type.PrincipalName()
		//        ;
		//    return typeName;
		//}

		#region Primitives
		public void WriteStatementTerminator()
		{
			this.WriteText(";");
		}
		#endregion

		protected void WriteDeclarator(string declarator)
		{
			if (declarator == null) throw new ArgumentNullException("declarator");
			this.Writer.WriteText(declarator, CodeTextType.Declarator);
		}
		protected void WriteIdentifier(string identifier, bool isDeclarator = false)
		{
			if (identifier == null) throw new ArgumentNullException("identifier");

			if (IsKeyword(identifier)) this.WriteText("@");

			if (isDeclarator)
				this.WriteDeclarator(identifier);
			else
				this.WriteText(identifier);
		}

		#region Macros
		public void WriteDeclaration(string[] keywords, Type type, string identifier)
		{
			if (
				(keywords != null)
				&& (keywords.Length > 0)
				)
			{
				this.WriteKeywordsWithTrailingSpace(keywords);
			}

			this.WriteMemberReference(type);
			this.WriteText(" ");
			this.WriteDeclarator(identifier);
		}
		#endregion

		#region Declarations
		public void WriteTypeDeclaration(Type type)
		{
			if (type == null) throw new ArgumentNullException("type");

			bool hasNamespace = (type.Namespace != null) && !type.IsNested;
			if (hasNamespace)
			{
				this.WriteKeywordWithTrailingSpace("namespace");
				this.WriteText(type.Namespace);
				this.OpenBlock();
			}

			this.WriteDeclarationAttributes(type, typeof(ExtensionAttribute));
			if (!type.IsAbstract && type.IsSubclassOf(typeof(Delegate)))
			{
				this.WriteDelegateDeclaration(type);
			}
			else if (type.IsClass)
			{
				this.WriteClassDeclaration(type);
			}
			else if (type.IsInterface)
			{
				this.WriteInterfaceDeclaration(type);
			}
			else if (type.IsEnum)
			{
				this.WriteEnumDeclaration(type);
			}
			else if (type.IsValueType)
			{
				this.WriteStructDeclaration(type);
			}

			if (hasNamespace)
			{
				this.CloseBlock();
			}
		}

		private void WriteStructDeclaration(Type type)
		{
			this.WriteComplexTypeDeclaration(type, "struct");
		}
		private void WriteInterfaceDeclaration(Type type)
		{
			this.WriteComplexTypeDeclaration(type, "interface");
		}

		private void WriteEnumDeclaration(Type type)
		{
			string typeVisibilityKeyword = this.GetTypeVisibilityKeyword(type);

			this.WriteKeywordsWithTrailingSpace(
				typeVisibilityKeyword,
				"enum"
				);

			this.WriteDeclarator(MemberViewInfo.GetMemberName(type));

			Type enumBaseType = type.GetEnumUnderlyingType();
			if (
				(enumBaseType != null)
				&& (enumBaseType != typeof(int))
				)
			{
				this.WriteText(" : ");
				this.WriteMemberReference(enumBaseType);
			}
			this.OpenBlock();

			if (!type.ContainsGenericParameters)
			{
				FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Public);
				bool isFlags = type.IsDefined(typeof(FlagsAttribute), true);
				foreach (FieldInfo field in fields)
				{
					long value = Convert.ToInt64(field.GetRawConstantValue());// (Enum)field.GetValue(null);
					this.WriteText(MemberViewInfo.GetMemberName(field));
					if (isFlags)
					{
						this.WriteText(" = 0x");
						this.WriteText(value.ToString("X"));
					}
					else
					{
						this.WriteText(" = ");
						this.WriteText(value.ToString("D"));
					}
					this.WriteText(",");
					this.Writer.WriteLineBreak();
				}
			}

			this.CloseBlock();
		}

		private void WriteDelegateDeclaration(Type type)
		{
			this.WriteDeclarationAttributes(type);

			string visibilityKeyword = GetTypeVisibilityKeyword(type);
			if (visibilityKeyword != null)
			{
				this.WriteKeywordWithTrailingSpace(visibilityKeyword);
			}
			this.WriteKeywordWithTrailingSpace("delegate");

			MethodInfo invokeMethod = type.GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public);
			this.WriteMemberReference(invokeMethod.ReturnType);
			this.WriteText(" ");
			this.WriteDeclarator(type.PrincipalName());
			if (type.IsGenericTypeDefinition)
				this.WriteGenericParameterList(type.GetGenericArguments(), true);

			this.WriteParameterList(invokeMethod);
			this.WriteStatementTerminator();
		}
		protected void WriteGenericConstraints(Type type)
		{
			if (type == null) throw new ArgumentNullException("type");

			if (!type.IsGenericTypeDefinition) return;

			Type[] genericParameters = type.GetGenericArguments();
			WriteGenericParametersConstraints(genericParameters);
		}

		private void WriteGenericParametersConstraints(Type[] genericParameters)
		{
			foreach (var genericParameter in genericParameters)
			{
				GenericParameterAttributes attributes = genericParameter.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;
				Type[] constraints = genericParameter.GetGenericParameterConstraints();

				bool hasConstraints = (constraints.Length > 0) || (attributes != 0);

				if (hasConstraints)
				{
					this.Writer.EnsureNewLine();
					this.WriteKeywordWithTrailingSpace("where");
					this.WriteText(MemberViewInfo.GetMemberName(genericParameter));
					this.WriteText(" : ");
				}

				bool first = true;
				foreach (var constraint in constraints)
				{
					if (first) first = false;
					else this.WriteText(", ");

					this.WriteMemberReference(constraint);
				}

				if (attributes != 0)
				{
					GenericParameterAttributes[] specialConstraintMasks = new GenericParameterAttributes[] {
						GenericParameterAttributes.NotNullableValueTypeConstraint,
						GenericParameterAttributes.ReferenceTypeConstraint,
						GenericParameterAttributes.DefaultConstructorConstraint,
					};

					foreach (var specialConstraintMask in specialConstraintMasks)
					{
						bool hasMask = (attributes.HasFlag(specialConstraintMask));
						if (hasMask)
						{
							if (first) first = false;
							else this.WriteText(", ");

							switch (specialConstraintMask)
							{
								case GenericParameterAttributes.DefaultConstructorConstraint:
									this.WriteKeywordText("new");
									this.WriteText("()");
									break;
								case GenericParameterAttributes.NotNullableValueTypeConstraint:
									this.WriteKeywordText("struct");
									break;
								case GenericParameterAttributes.ReferenceTypeConstraint:
									this.WriteKeywordText("class");
									break;
							}
						}
					}
				}
			}
		}
		private void WriteGenericParameterList(Type[] genericParameters, bool isDefinition)
		{
			this.WriteText("<");

			bool first = true;
			foreach (var genericParameter in genericParameters)
			{
				if (first) first = false;
				else this.WriteText(", ");

				if (isDefinition)
				{
					GenericParameterAttributes genericAttributes = genericParameter.GenericParameterAttributes;
					if (genericAttributes.HasFlag(GenericParameterAttributes.Covariant))
					{
						this.WriteKeywordWithTrailingSpace("out");
					}
					else if (genericAttributes.HasFlag(GenericParameterAttributes.Contravariant))
					{
						this.WriteKeywordWithTrailingSpace("in");
					}
				}

				if (isDefinition)
					this.WriteText(MemberViewInfo.GetMemberName(genericParameter));
				else
					this.WriteMemberReference(genericParameter);
			}

			this.WriteText(">");
		}
		protected void WriteComplexTypeDeclaration(
			Type type,
			string specifierKeyword
			)
		{
			bool isExtension = type.IsExtension();

			this.WriteKeywordsWithTrailingSpace(
				GetTypeVisibilityKeyword(type),
				(isExtension ? "static" : ((type.IsAbstract && type.IsClass) ? "abstract" : null)),
				specifierKeyword
				);
			this.WriteDeclarator(type.PrincipalName());
			if (type.IsGenericTypeDefinition)
				this.WriteGenericParameterList(type.GetGenericArguments(), true);

			List<Type> baseTypes = new List<Type>();
			if (
				(type.BaseType != null)
				&& (!MemberEqualityComparer.Equal(type.BaseType, typeof(object)))
				&& (!MemberEqualityComparer.Equal(type.BaseType, typeof(ValueType)))
				&& (!MemberEqualityComparer.Equal(type.BaseType, typeof(int)))
				)
			{
				baseTypes.Add(type.BaseType);
			}
			Type[] allImplementedInterfaces = type.GetInterfaces();
			Type[] baseImplementedInterfaces =
				(type.BaseType != null)
				? type.BaseType.GetInterfaces()
				: new Type[0]
				;
			Type[] implementedInterfaces = allImplementedInterfaces.Except(baseImplementedInterfaces).ToArray();

			baseTypes.AddRange(implementedInterfaces);
			if (baseTypes.Count > 0)
			{
				this.WriteText(" : ");
				bool first = true;
				if (baseTypes.Count > 1)
				{
					this.Writer.Indent();
					foreach (var baseType in baseTypes)
					{
						if (first) first = false;
						else this.WriteText(", ");

						this.Writer.WriteLineBreak();
						this.WriteMemberReference(baseType);
					}
					this.Writer.Outdent();
				}
				else
				{
					foreach (var baseType in baseTypes)
					{
						if (first) first = false;
						else this.WriteText(", ");
						this.WriteMemberReference(baseType);
					}
				}
			}

			this.Writer.Indent();
			this.WriteGenericConstraints(type);
			this.Writer.Outdent();

			this.OpenBlock();

			var members = type.GetMembers(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
			foreach (var member in members)
			{
				bool isHidden = member.IsHiddenClassMember();
				if (!isHidden)
				{
					this.Writer.EnsureNewLine();
					this.WriteMemberDeclaration(member);
				}
			}

			this.CloseBlock();
		}
		protected void WriteClassDeclaration(Type type)
		{
			this.WriteComplexTypeDeclaration(type, "class");
		}

		public void WriteMemberDeclaration(MemberInfo member)
		{
			if (member == null) throw new ArgumentNullException("member");

			Type type = member as Type;
			if (type != null)
			{
				this.WriteTypeDeclaration(type);
			}
			else
			{
				MethodInfo method = member as MethodInfo;
				if (method != null)
				{
					this.WriteMethodDeclaration(method);
				}
				else
				{
					PropertyInfo property = member as PropertyInfo;
					if (property != null)
					{
						this.WritePropertyDeclaration(property);
					}
					else
					{
						FieldInfo field = member as FieldInfo;
						if (field != null)
						{
							this.WriteFieldDeclaration(field);
						}
						else
						{
							ConstructorInfo constructor = member as ConstructorInfo;
							if (constructor != null)
							{
								this.WriteConstructorDeclaration(constructor);
							}
							else
							{
								EventInfo eventInfo = member as EventInfo;
								if (eventInfo != null)
								{
									this.WriteEventDeclaration(eventInfo);
								}
								else
								{
									throw new ArgumentException("Unknown member type");
								}
							}
						}
					}
				}
			}
		}
		private Expression AttributeArgumentToExpression(Type argumentType, object value)
		{
			Expression argumentExpression;
			if (argumentType.IsEnum)
			{
				argumentExpression = MsilDecompiler.CoerceExpression(ConstantExpression.FromValue(value), argumentType, null);
			}
			else if (MemberEqualityComparer.Equal(argumentType, typeof(Type)))
			{
				argumentExpression = new TypeReferenceExpression((Type)value);
			}
			else if (argumentType.IsArray && (value != null))
			{
				ICollection argumentElements = (ICollection)value;
				List<Expression> elements = new List<Expression>(argumentElements.Count);
				Type elementType = argumentType.GetElementType();
				foreach (var element in argumentElements)
				{
					Expression elementExpression = AttributeArgumentToExpression(elementType, element);
					elements.Add(elementExpression);
				}
				NewImplicitArrayExpression arrayExpression = new NewImplicitArrayExpression(elementType, elements);
				argumentExpression = arrayExpression;
			}
			else
			{
				argumentExpression = ConstantExpression.FromValue(value);
			}

			return argumentExpression;
		}
		private void WriteAttributeArgument(CustomAttributeTypedArgument argument)
		{
			Expression argumentExpression = AttributeArgumentToExpression(argument.ArgumentType, argument.Value);
			this.WriteExpression(argumentExpression);
		}
		private void WriteAttributeArgument(CustomAttributeNamedArgument argument)
		{
			this.WriteMemberReference(argument.MemberInfo);
			this.WriteText(" = ");
			this.WriteAttributeArgument(argument.TypedValue);
		}
		private void WriteAttributes(IEnumerable<CustomAttributeData> attributes, string prefix = null, params Type[] typesToIgnore)
		{
			foreach (var attribute in attributes)
			{
				bool ignore = (typesToIgnore != null) && (typesToIgnore.Contains(attribute.Constructor.ReflectedType));
				if (!ignore)
					this.WriteAttribute(attribute, prefix);
			}
		}
		private void WriteAttribute(CustomAttributeData attribute, string prefix = null)
		{
			this.WriteText("[");
			if (prefix != null)
			{
				this.WriteKeywordText(prefix);
				this.WriteText(":");
			}

			this.WriteTypeReference(attribute.Constructor, MemberReferenceFlags.Attribute);

			if (
				(attribute.ConstructorArguments.Count > 0)
				|| (attribute.NamedArguments.Count > 0)
				)
			{
				ConstructorInfo attributeConstructor = attribute.Constructor;
				ParameterInfo[] constructorParameters = attributeConstructor.GetParameters();

				this.WriteText("(");
				bool first = true;
				//for (int i = 0; i < constructorParameters.Length; i++)
				//{

				//    ParameterInfo parameter = constructorParameters[i];
				//    CustomAttributeTypedArgument argument = attribute.ConstructorArguments[i];
				//}

				foreach (var argument in attribute.ConstructorArguments)
				{
					if (first) first = false;
					else this.WriteText(", ");
					this.WriteAttributeArgument(argument);
				}
				foreach (var namedArgument in attribute.NamedArguments)
				{
					if (first) first = false;
					else this.WriteText(", ");
					this.WriteAttributeArgument(namedArgument);
				}
				this.WriteText(")");
			}

			this.WriteText("]");
			this.Writer.WriteLineBreak();
		}
		public void WriteDeclarationAttributes(Assembly assembly)
		{
			if (assembly == null) throw new ArgumentNullException("assembly");

			var customAttributeData = assembly.GetCustomAttributesData();
			this.WriteAttributes(customAttributeData, "assembly");
		}
		public void WriteDeclarationAttributes(MemberInfo member, params Type[] typesToIgnore)
		{
			if (member == null) throw new ArgumentNullException("member");

			var customAttributeData = member.GetCustomAttributesData();
			this.WriteAttributes(customAttributeData, null, typesToIgnore);
		}
		public void WriteFieldDeclaration(FieldInfo field)
		{
			if (field == null) throw new ArgumentNullException("field");

			this.WriteDeclarationAttributes(field);

			string accessKeyword = this.GetMemberAccessKeyword((int)(field.Attributes & FieldAttributes.FieldAccessMask));
			bool isStatic = field.Attributes.HasFlag(FieldAttributes.Static);
			bool isLiteral = isStatic && field.Attributes.HasFlag(FieldAttributes.Literal);
			string specifierKeyword = null;
			if (isStatic)
			{
				if (isLiteral)
				{
					specifierKeyword = "const";
				}
				else
				{
					specifierKeyword = "static";
				}
			}
			string readonlyKeyword = (!isLiteral && field.Attributes.HasFlag(FieldAttributes.InitOnly))
				? "readonly"
				: null;

			this.WriteDeclaration(
				new string[] {
					accessKeyword,
					specifierKeyword,
					readonlyKeyword
					},
				field.FieldType,
				MemberViewInfo.GetMemberName(field)
				);

			if (isLiteral)
			{
				this.WriteText(" = ");
				object literalValue = field.GetRawConstantValue();
				this.WriteValue(literalValue);
			}
			this.WriteStatementTerminator();
		}

		public void WritePropertyDeclaration(PropertyInfo property)
		{
			if (property == null) throw new ArgumentNullException("property");

			this.WriteDeclarationAttributes(property);

			MethodInfo[] accessors = property.GetAccessors(true);
			int leastRestrictiveAccess = 0;
			foreach (var accessor in accessors)
			{
				int access = (int)(accessor.Attributes & MethodAttributes.MemberAccessMask);
				leastRestrictiveAccess = Math.Max(access, leastRestrictiveAccess);
			}

			MethodInfo accessor0 = accessors[0];

			string accessKeyword = (property.ReflectedType.IsInterface || property.IsPrivateInterfaceImplementation()) ? null : this.GetMemberAccessKeyword(leastRestrictiveAccess);
			string vtableKeyword = (property.ReflectedType.IsInterface || property.IsPrivateInterfaceImplementation()) ? null : this.GetVTableKeyword(accessor0);

			bool isIndexer = property.IsIndexer();
			if (isIndexer)
			{
				this.WriteKeywordsWithTrailingSpace(accessKeyword, vtableKeyword);
				this.WriteTypeReference(property.PropertyType);
				this.WriteText(" ");
				this.WriteKeywordText("this");
				this.WriteText("[");
				this.WriteParameterList(property.GetGetMethod(true), true);
				this.WriteText("]");
			}
			else
			{
				this.WriteDeclaration(
					new string[] {
						accessKeyword,
						vtableKeyword
					},
					property.PropertyType,
					MemberViewInfo.GetMemberName(property)
					);
			}

			bool hasBackingField = property.HasBackingField();
			bool hasBody = !hasBackingField && MsilDecompiler.HasMethodBody(accessor0);

			if (hasBody)
			{
				this.OpenBlock();
			}
			else
			{
				this.OpenInlineBlock();
			}

			MethodInfo getAccessor = property.GetGetMethod(true);
			bool hasPreceedingGetAccessorStub = true;
			if (getAccessor != null)
			{
				int access = (int)(getAccessor.Attributes & MethodAttributes.MemberAccessMask);
				if (access != leastRestrictiveAccess)
				{
					this.WriteKeywordText(this.GetMemberAccessKeyword(access));
				}
				this.Writer.WriteMemberReference(getAccessor, "get", CodeTextType.Keyword);

				if (hasBody)
				{
					this.WriteMethodBody(getAccessor);
				}
				else
				{
					hasPreceedingGetAccessorStub = true;
					this.WriteStatementTerminator();
				}
			}

			MethodInfo setAccessor = property.GetSetMethod(true);
			if (setAccessor != null)
			{
				if (hasPreceedingGetAccessorStub)
				{
					this.WriteText(" ");
				}

				int access = (int)(setAccessor.Attributes & MethodAttributes.MemberAccessMask);
				if (access != leastRestrictiveAccess)
				{
					this.WriteKeywordText(this.GetMemberAccessKeyword(access));
				}

				if (getAccessor != null)
				{
					this.WriteText(" ");
				}
				this.Writer.WriteMemberReference(setAccessor, "set", CodeTextType.Keyword);
				if (hasBody)
				{
					this.WriteMethodBody(setAccessor);
				}
				else
				{
					this.WriteStatementTerminator();
				}
			}

			if (hasBody)
			{
				this.CloseBlock();
			}
			else
			{
				this.CloseInlineBlock();
			}
		}


		public void WriteEventDeclaration(EventInfo eventInfo)
		{
			if (eventInfo == null) throw new ArgumentNullException("eventInfo");

			this.WriteDeclarationAttributes(eventInfo);

			MethodInfo addMethod = eventInfo.GetAddMethod(true);
			MethodInfo removeMethod = eventInfo.GetRemoveMethod(true);

			//int leastRestrictiveAccess = 0;
			//foreach (var accessor in accessors)
			//{
			//    int access = (int)(accessor.Attributes & MethodAttributes.MemberAccessMask);
			//    leastRestrictiveAccess = Math.Max(access, leastRestrictiveAccess);
			//}
			int access = (int)(addMethod.Attributes & MethodAttributes.MemberAccessMask);
			string accessKeyword = (eventInfo.ReflectedType.IsInterface) ? null : this.GetMemberAccessKeyword(access);

			this.WriteDeclaration(
				new string[] {
					accessKeyword,
					"event",
					this.GetVTableKeyword(addMethod)
				},
				eventInfo.EventHandlerType,
				MemberViewInfo.GetMemberName(eventInfo)
				);

			bool hasBackingField = eventInfo.HasBackingField();
			bool hasBody = !hasBackingField && MsilDecompiler.HasMethodBody(addMethod);

			if (hasBackingField)
			{
				this.WriteStatementTerminator();
			}
			else
			{
				this.OpenBlock();

				this.Writer.WriteMemberReference(eventInfo.GetAddMethod(), "add", CodeTextType.Keyword);
				if (MsilDecompiler.HasMethodBody(addMethod))
				{
					this.OpenBlock();
					this.WriteMethodBody(addMethod);
					this.CloseBlock();
				}
				else
				{
					this.WriteText("; ");
				}

				this.Writer.WriteMemberReference(eventInfo.GetRemoveMethod(), "remove", CodeTextType.Keyword);
				if (MsilDecompiler.HasMethodBody(removeMethod))
				{
					this.OpenBlock();
					this.WriteMethodBody(removeMethod);
					this.CloseBlock();
				}
				else
				{
					this.WriteText("; ");
				}

				this.CloseBlock();
			}
		}
		protected void WriteMethodBody(MethodBase method)
		{
			if (this.DisassemblerDelegate == null) throw new InvalidOperationException("Cannot write method body when DisassemblerDelegate is null");

			MethodDecompilation decompilation = null;
			try
			{
				decompilation = this.DisassemblerDelegate(method);
			}
			catch
			{
				// TODO: Write decompilation error
			}
			if (decompilation == null) return;
			var statements = decompilation.Statements;
			if (statements == null) return;

			if (decompilation.ChainedConstructor != null)
			{
				this.Writer.Indent();
				this.Writer.EnsureNewLine();
				this.WriteText(": ");

				string chainedConstructorKeyword =
					(decompilation.ChainedConstructor.ReflectedType == method.ReflectedType)
					? "this"
					: "base"
					;
				this.Writer.WriteMemberReference(decompilation.ChainedConstructor, chainedConstructorKeyword);

				this.WriteText("(");
				if (decompilation.ChainedArguments != null)
				{
					this.WriteExpressionList(decompilation.ChainedArguments);
				}
				this.WriteText(")");

				this.Writer.Outdent();
			}
			else if (decompilation.InitializesObject)
			{
				this.Writer.EnsureNewLine();
				this.WriteText(": ");

				this.WriteKeywordText("this");
				this.WriteText("()");
			}

			this.OpenBlock();

			// Write method body
			this.OnMethodBodyStart(method, method.GetMethodBody(), decompilation);
			this.WriteStatementBlock(statements);
			this.OnMethodBodyEnd(method, method.GetMethodBody(), decompilation);

			this.CloseBlock();
		}
		protected void WriteGenericConstraints(MethodInfo method)
		{
			if (method == null) throw new ArgumentNullException("method");

			if (!method.IsGenericMethodDefinition) return;

			Type[] genericParameters = method.GetGenericArguments();
			WriteGenericParametersConstraints(genericParameters);
		}

		public void WriteMethodDeclaration(MethodInfo method)
		{
			if (method == null) throw new ArgumentNullException("method");

			this.WriteDeclarationAttributes(method, typeof(ExtensionAttribute));
			MethodImplAttributes implFlags = method.GetMethodImplementationFlags();
			if (implFlags != 0)
			{
				this.WriteText("[");
				this.WriteTypeReference(typeof(MethodImplAttribute), MemberReferenceFlags.Attribute);
				this.WriteArgumentList(typeof(MethodImplAttribute).GetConstructor(new Type[] { typeof(MethodImplOptions) }).GetParameters(), new Expression[] { ConstantExpression.FromValue((MethodImplOptions)implFlags) });
				this.WriteText("]");
				this.Writer.WriteLineBreak();
			}

			if (method.ReturnParameter != null)
			{
				var customAttributesData = method.ReturnParameter.GetCustomAttributesData();
				this.WriteAttributes(customAttributesData, "return");
			}

			if (!method.IsPrivateInterfaceImplementation())
			{
				int access = (int)(method.Attributes & MethodAttributes.MemberAccessMask);
				string accessKeyword = (method.ReflectedType.IsInterface) ? null : this.GetMemberAccessKeyword(access);
				string vtableKeyword = (method.ReflectedType.IsInterface) ? null : this.GetVTableKeyword(method);

				this.WriteKeywordsWithTrailingSpace(
						accessKeyword,
						vtableKeyword
					);
			}

			bool isImplicit = (method.Name == CSharpOperatorOverloads.ImplicitOverloadName);
			bool isExplicit = (method.Name == CSharpOperatorOverloads.ExplicitOverloadName);
			bool isUnaryOverload = CSharpOperatorOverloads.IsUnaryOperatorOverload(method);
			bool isBinaryOverload = CSharpOperatorOverloads.IsBinaryOperatorOverload(method);

			if (isImplicit || isExplicit)
			{
				string conversionKeyword = (isImplicit ? "implicit" : "explicit");
				this.WriteKeywordsWithTrailingSpace(
					conversionKeyword,
					"operator"
					);
				this.WriteMemberReference(method.ReturnType);
			}
			else if (isUnaryOverload || isBinaryOverload)
			{
				ExpressionOperator op = (isBinaryOverload ? CSharpOperatorOverloads.GetBinaryOperator(method) : CSharpOperatorOverloads.GetUnaryOperator(method));
				this.WriteMemberReference(method.ReturnType);
				this.WriteText(" ");
				this.WriteKeywordWithTrailingSpace("operator");
				this.WriteOperator(op);
			}
			else
			{
				this.WriteMemberReference(method.ReturnType);
				this.WriteText(" ");
				this.WriteDeclarator(MemberViewInfo.GetMemberName(method));

				if (method.IsGenericMethodDefinition)
					this.WriteGenericParameterList(method.GetGenericArguments(), true);
			}

			this.WriteParameterList(method);

			this.WriteGenericConstraints(method);

			if (MsilDecompiler.HasMethodBody(method) && this.DisassemblerDelegate != null)
			{
				this.WriteMethodBody(method);
			}
			else
			{
				this.WriteStatementTerminator();
				this.Writer.WriteLineBreak();
			}
		}
		public void WriteConstructorDeclaration(ConstructorInfo constructor)
		{
			if (constructor == null) throw new ArgumentNullException("constructor");

			this.WriteDeclarationAttributes(constructor);

			int access = (int)(constructor.Attributes & MethodAttributes.MemberAccessMask);

			string accessKeyword = (constructor.IsStatic ? null : this.GetMemberAccessKeyword(access));
			string vtableKeyword = (constructor.IsStatic ? "static" : null);

			this.WriteKeywordsWithTrailingSpace(accessKeyword, vtableKeyword);
			this.WriteDeclarator(constructor.ReflectedType.PrincipalName());

			this.WriteParameterList(constructor);

			if (MsilDecompiler.HasMethodBody(constructor))
			{
				this.WriteMethodBody(constructor);
			}
			else
			{
				this.WriteStatementTerminator();
				this.Writer.WriteLineBreak();
			}
		}
		private void WriteParameterDeclaration(ParameterInfo parameter, bool isExtensionInstance = false)
		{
			var parameterAttributeData = parameter.GetCustomAttributesData();
			this.WriteAttributes(parameterAttributeData, null, typeof(ParamArrayAttribute), typeof(OutAttribute));

			Type parameterType = parameter.ParameterType;
			bool isParamArray = parameter.IsParamArray();
			List<string> keywords = new List<string>();
			if (isParamArray) keywords.Add("params");
			if (isExtensionInstance) keywords.Add("this");

			bool isReference = parameterType.IsByRef;
			if (isReference)
			{
				if (parameter.IsIn)
				{
					keywords.Add("ref");
				}
				else
				{
					keywords.Add("out");
				}

				if (parameterType.HasElementType)
					parameterType = parameterType.GetElementType();
			}

			this.WriteKeywordsWithTrailingSpace(keywords);
			this.WriteMemberReference(parameterType);
			if (parameter.Name != null)
			{
				this.WriteText(" ");
				this.WriteIdentifier(parameter.Name, true);

				if (parameter.RawDefaultValue != DBNull.Value)
				{
					this.WriteText(" = ");
					this.WriteValue(parameter.DefaultValue);
				}
			}
		}
		private void WriteParameterList(MethodBase method, bool omitParentheses = false)
		{
			bool isExtensionMethod = method.IsExtension();

			if (!omitParentheses) this.WriteText(" (");

			ParameterInfo[] parameters = method.GetParameters();
			bool hasParametersWithAttributes = parameters.Any(param => param.GetCustomAttributesData().Count > 0);

			if (parameters.Length == 1 && !hasParametersWithAttributes)
			{
				this.WriteParameterDeclaration(parameters[0], isExtensionMethod);
			}
			else if (parameters.Length > 0)
			{
				this.Writer.WriteLineBreak();
				this.Writer.Indent();
				foreach (var parameter in parameters)
				{
					bool isInstance = false;
					if (parameter.Position > 0)
					{
						this.WriteText(",");
						this.Writer.WriteLineBreak();
					}
					else
					{
						isInstance = isExtensionMethod;
					}

					this.WriteParameterDeclaration(parameter, isInstance);
				}
				this.Writer.WriteLineBreak();
				this.Writer.Outdent();
			}

			if (!omitParentheses) this.WriteText(")");
		}
		#endregion

		#region Statements
		private IDictionary<Type, Action<Statement>> StatementWriterTable { get; set; }
		protected static Action<Statement> CreateStatementWriterThunk<TStatement>(Action<TStatement> writer)
			where TStatement : Statement
		{
			return writer.CreateThunk<TStatement, Statement>();
		}
		protected Action<Statement> GetStatementWriter(Statement statement)
		{
			if (statement == null) throw new ArgumentNullException("statement");

			Type type = statement.GetType();
			while (type != null)
			{
				Action<Statement> writer;
				if (this.StatementWriterTable.TryGetValue(type, out writer)) return writer;

				type = type.BaseType;
			}

			return null;
		}
		protected virtual void DefineStatementWriters(IDictionary<Type, Action<Statement>> writerTable)
		{
			writerTable[typeof(AssignmentStatement)] = CreateStatementWriterThunk<AssignmentStatement>(this.WriteAssignmentStatement);
			writerTable[typeof(EmptyStatement)] = CreateStatementWriterThunk<EmptyStatement>(this.WriteEmptyStatement);
			writerTable[typeof(ExpressionStatement)] = CreateStatementWriterThunk<ExpressionStatement>(this.WriteExpressionStatement);
			writerTable[typeof(LoopStatement)] = CreateStatementWriterThunk<LoopStatement>(this.WriteLoopStatement);
			writerTable[typeof(ConditionStatement)] = CreateStatementWriterThunk<ConditionStatement>(this.WriteConditionStatement);
			writerTable[typeof(ReturnStatement)] = CreateStatementWriterThunk<ReturnStatement>(this.WriteReturnStatement);
			writerTable[typeof(BranchStatement)] = CreateStatementWriterThunk<BranchStatement>(this.WriteBranchStatement);
			writerTable[typeof(ConditionalBranchStatement)] = CreateStatementWriterThunk<ConditionalBranchStatement>(this.WriteConditionalBranchStatement);
			writerTable[typeof(ThrowStatement)] = CreateStatementWriterThunk<ThrowStatement>(this.WriteThrowStatement);
			writerTable[typeof(VariableDeclarationStatement)] = CreateStatementWriterThunk<VariableDeclarationStatement>(this.WriteVariableDeclarationStatement);
			writerTable[typeof(SwitchStatement)] = CreateStatementWriterThunk<SwitchStatement>(this.WriteSwitchStatement);
			writerTable[typeof(LoopContinueStatement)] = CreateStatementWriterThunk<LoopContinueStatement>(this.WriteLoopContinueStatement);
			writerTable[typeof(LoopExitStatement)] = CreateStatementWriterThunk<LoopExitStatement>(this.WriteLoopExitStatement);
			writerTable[typeof(TryBlock)] = CreateStatementWriterThunk<TryBlock>(this.WriteTryBlock);
			writerTable[typeof(IteratorLoop)] = CreateStatementWriterThunk<IteratorLoop>(this.WriteIteratorLoop);
			writerTable[typeof(EventHandlerStatement)] = CreateStatementWriterThunk<EventHandlerStatement>(this.WriteEventHandlerStatement);
		}
		private void WriteStatementBlock(IEnumerable<Statement> statements)
		{
			if (statements == null) throw new ArgumentNullException("statements");

			foreach (var statement in statements)
			{
				this.WriteStatement(statement);
			}
		}

		public void WriteStatement(Statement statement)
		{
			this.Writer.EnsureNewLine();
			this.Writer.EnsureIndent();

			this.OnStatementStart(statement);
			this.WriteStatementBody(statement);
			this.OnStatementEnd(statement);
		}
		private void WriteStatementBody(Statement statement)
		{
			if (statement == null) throw new ArgumentNullException("statement");

			Action<Statement> writer = this.GetStatementWriter(statement);
			if (writer == null) throw new ArgumentException(string.Format(Messages.InvalidStatementTypeMessagePattern, statement.GetType().FullName));

			writer(statement);
		}

		private void WriteEventHandlerStatement(EventHandlerStatement eventHandlerStatement)
		{
			if (eventHandlerStatement.Instance != null)
				this.WriteExpressionWithin(eventHandlerStatement.Instance, null);
			else
				this.WriteTypeReference(eventHandlerStatement.Event.ReflectedType);

			this.WriteText(".");
			this.WriteMemberReference(eventHandlerStatement.Event);
			this.WriteText(" ");

			switch (eventHandlerStatement.Operation)
			{
				case EventHandlerOperation.Attach:
					this.WriteText("+=");
					break;
				case EventHandlerOperation.Detach:
					this.WriteText("-=");
					break;
				case EventHandlerOperation.Other:
				case EventHandlerOperation.Raise:
				default:
					break;
			}

			this.WriteText(" ");
			this.WriteExpression(eventHandlerStatement.Handler);
			this.WriteStatementTerminator();
		}
		private void WriteIteratorLoop(IteratorLoop iteratorLoop)
		{
			this.WriteKeywordText("foreach");
			this.WriteText(" (");
			this.WriteMemberReference(iteratorLoop.ElementVariable.StaticVariableType);
			this.WriteText(" ");
			this.WriteIdentifier(iteratorLoop.ElementVariable.Name);
			this.WriteText(" ");
			this.WriteKeywordWithTrailingSpace("in");
			this.WriteExpression(iteratorLoop.Source);
			this.WriteText(")");
			this.OnStatementHeaderEnd(iteratorLoop);
			this.OpenBlock();
			this.WriteStatementBlock(iteratorLoop.Statements);
			this.CloseBlock();
		}
		private void WriteTryBlock(TryBlock tryBlock)
		{
			this.WriteKeywordText("try");
			this.OnStatementHeaderEnd(tryBlock);
			this.OpenBlock();
			this.WriteStatementBlock(tryBlock.TryStatements);
			this.CloseBlock();
			foreach (var catchBlock in tryBlock.CatchBlocks)
			{
				this.WriteKeywordText("catch");
				if (catchBlock.ExceptionVariable != null)
				{
					this.WriteText(" (");
					this.WriteMemberReference(catchBlock.ExceptionVariable.LocalInfo.LocalType);
					this.WriteText(" ");
					this.WriteIdentifier(catchBlock.ExceptionVariable.Name);
					this.WriteText(")");
				}
				this.OpenBlock();
				this.WriteStatementBlock(catchBlock.Statements);
				this.CloseBlock();
			}
			if (tryBlock.FinallyStatements.Count > 0)
			{
				this.WriteKeywordText("finally");
				this.OpenBlock();
				this.WriteStatementBlock(tryBlock.FinallyStatements);
				this.CloseBlock();
			}
		}
		private void WriteLoopExitStatement(LoopExitStatement loopExitStatement)
		{
			this.WriteKeywordText("break");
			this.WriteStatementTerminator();
			this.Writer.WriteLineBreak();
		}
		private void WriteLoopContinueStatement(LoopContinueStatement loopContinueStatement)
		{
			this.WriteKeywordText("continue");
			this.WriteStatementTerminator();
			this.Writer.WriteLineBreak();
		}
		private void WriteSwitchStatement(SwitchStatement switchStatement)
		{
			this.WriteKeywordText("switch");
			this.WriteText(" (");
			this.WriteExpression(switchStatement.SwitchExpression);
			this.WriteText(")");
			this.OpenBlock();

			foreach (var caseBlock in switchStatement.CaseBlocks)
			{
				this.WriteCaseBlock(caseBlock);
			}
			this.CloseBlock();
		}
		private void WriteCaseBlock(CaseBlock caseBlock)
		{
			if (caseBlock.Label == null)
			{
				this.WriteKeywordText("default");
			}
			else
			{
				this.WriteKeywordWithTrailingSpace("case");
				this.WriteExpression(caseBlock.Label);
			}
			this.WriteText(":");
			this.Writer.WriteLineBreak();
			this.Writer.Indent();

			this.WriteStatementBlock(caseBlock.Statements);
			this.Writer.Outdent();
		}

		private void WriteVariableDeclarationStatement(VariableDeclarationStatement declarationStatement)
		{
			Type variableType = declarationStatement.Variable.StaticVariableType;
			this.WriteMemberReference(variableType);
			this.WriteText(" ");
			this.WriteIdentifier(declarationStatement.Variable.Name, true);
			if (declarationStatement.Initializer != null)
			{
				this.WriteText(" = ");
				this.WriteExpression(declarationStatement.Initializer);
			}

			this.WriteStatementTerminator();
			this.Writer.WriteLineBreak();
		}
		private void WriteThrowStatement(ThrowStatement throwStatement)
		{
			this.WriteKeywordText("throw");
			if (throwStatement.Exception != null)
			{
				this.WriteText(" ");
				this.WriteExpression(throwStatement.Exception);
			}
			this.WriteStatementTerminator();
			this.Writer.WriteLineBreak();
		}
		private void WriteBranchStatement(BranchStatement branchStatement)
		{
			this.WriteKeywordWithTrailingSpace("goto");
			this.WriteIdentifier(branchStatement.Target.Name);
			this.WriteStatementTerminator();
			this.Writer.WriteLineBreak();
		}
		private void WriteConditionalBranchStatement(ConditionalBranchStatement conditionalBranchStatement)
		{
			this.WriteKeywordText("if");
			this.WriteText(" (");
			this.WriteExpression(conditionalBranchStatement.Condition);
			this.WriteText(") ");
			this.WriteBranchStatement(conditionalBranchStatement);
		}
		private void WriteAssignmentStatement(AssignmentStatement assignmentStatement)
		{
			this.WriteExpression((Expression)assignmentStatement.Variable);
			this.WriteText(" = ");
			this.WriteExpression(assignmentStatement.Initializer);
			this.WriteStatementTerminator();
			this.Writer.WriteLineBreak();
		}
		private void WriteEmptyStatement(EmptyStatement emptyStatement)
		{
		}
		private void WriteExpressionStatement(ExpressionStatement expressionStatement)
		{
			this.WriteExpression(expressionStatement.Expression);
			this.WriteStatementTerminator();
			this.Writer.WriteLineBreak();
		}
		private void WriteLoopCondition(Expression condition)
		{
			this.WriteKeywordText("while");
			this.WriteText(" (");
			this.WriteExpression(condition);
			this.WriteText(")");
		}
		private void WriteLoopStatement(LoopStatement loopStatement)
		{
			if (loopStatement.EvaluateInitially)
			{
				this.WriteLoopCondition(loopStatement.Condition);
			}
			else
			{
				this.WriteKeywordText("do");
			}
			this.OnStatementHeaderEnd(loopStatement);

			this.OpenBlock();
			this.WriteStatementBlock(loopStatement.Statements);
			this.CloseBlock();

			if (!loopStatement.EvaluateInitially)
			{
				this.WriteLoopCondition(loopStatement.Condition);
				this.WriteStatementTerminator();
				this.Writer.WriteLineBreak();
			}
		}
		private void WriteConditionStatement(ConditionStatement conditionStatement)
		{
			this.WriteKeywordText("if");
			this.WriteText(" (");
			this.WriteExpression(conditionStatement.Condition);
			this.WriteText(")");
			this.OnStatementHeaderEnd(conditionStatement);

			this.OpenBlock();
			this.WriteStatementBlock(conditionStatement.TrueStatements);
			this.CloseBlock();

			if (conditionStatement.FalseStatements.Count > 0)
			{
				this.WriteKeywordText("else");
				ConditionStatement nestedConditionBlock = conditionStatement.FalseStatements[0] as ConditionStatement;
				if (
					(nestedConditionBlock != null)
					&& (conditionStatement.FalseStatements.Count == 1)
					)
				{
					this.WriteText(" ");
					this.WriteConditionStatement(nestedConditionBlock);
				}
				else
				{
					this.OpenBlock();
					this.WriteStatementBlock(conditionStatement.FalseStatements);
					this.CloseBlock();
				}
			}
		}
		private void WriteReturnStatement(ReturnStatement returnStatement)
		{
			this.WriteKeywordText("return");
			if (returnStatement.Expression != null)
			{
				this.WriteText(" ");
				this.WriteExpression(returnStatement.Expression);
			}

			this.WriteStatementTerminator();
			this.Writer.WriteLineBreak();
		}
		#endregion

		#region Expressions
		private IDictionary<Type, Action<Expression>> ExpressionWriterTable { get; set; }
		private static Action<Expression> CreateExpressionWriterThunk<TExpression>(Action<TExpression> writer)
			where TExpression : Expression
		{
			return writer.CreateThunk<TExpression, Expression>();
		}
		protected Action<Expression> GetExpressionWriter(Expression expression)
		{
			if (expression == null) throw new ArgumentNullException("expression");

			Type type = expression.GetType();
			while (type != null)
			{
				Action<Expression> writer;
				if (this.ExpressionWriterTable.TryGetValue(type, out writer)) return writer;

				type = type.BaseType;
			}

			return null;
		}
		protected void InitializeExpressionWriterTable()
		{
			Dictionary<Type, Action<Expression>> writerTable = new Dictionary<Type, Action<Expression>>();

			writerTable[typeof(VariableReferenceExpression)] = CreateExpressionWriterThunk<VariableReferenceExpression>(this.WriteVariableReferenceExpression);
			writerTable[typeof(BinaryOperationExpression)] = CreateExpressionWriterThunk<BinaryOperationExpression>(this.WriteBinaryOperationExpression);
			writerTable[typeof(BoundConstructorExpression)] = CreateExpressionWriterThunk<BoundConstructorExpression>(this.WriteBoundConstructorExpression);
			writerTable[typeof(BoundFunctionCallExpression)] = CreateExpressionWriterThunk<BoundFunctionCallExpression>(this.WriteBoundFunctionCallExpression);
			writerTable[typeof(IndirectCallExpression)] = CreateExpressionWriterThunk<IndirectCallExpression>(this.WriteIndirectCallExpression);
			writerTable[typeof(CastExpression)] = CreateExpressionWriterThunk<CastExpression>(this.WriteCastExpression);
			writerTable[typeof(ConstantExpression)] = CreateExpressionWriterThunk<ConstantExpression>(this.WriteConstantExpression);
			writerTable[typeof(FieldReferenceExpression)] = CreateExpressionWriterThunk<FieldReferenceExpression>(this.WriteFieldReferenceExpression);
			writerTable[typeof(InstanceReferenceExpression)] = CreateExpressionWriterThunk<InstanceReferenceExpression>(this.WriteInstanceReferenceExpression);
			writerTable[typeof(NewArrayExpression)] = CreateExpressionWriterThunk<NewArrayExpression>(this.WriteNewArrayExpression);
			writerTable[typeof(PropertyReferenceExpression)] = CreateExpressionWriterThunk<PropertyReferenceExpression>(this.WritePropertyReferenceExpression);
			writerTable[typeof(ConditionalExpression)] = CreateExpressionWriterThunk<ConditionalExpression>(this.WriteConditionalExpression);
			writerTable[typeof(AssignmentExpression)] = CreateExpressionWriterThunk<AssignmentExpression>(this.WriteAssignmentExpression);
			writerTable[typeof(NewImplicitArrayExpression)] = CreateExpressionWriterThunk<NewImplicitArrayExpression>(this.WriteNewImplicitArrayExpression);
			writerTable[typeof(IndexerExpression)] = CreateExpressionWriterThunk<IndexerExpression>(this.WriteIndexerExpression);
			writerTable[typeof(MethodReferenceExpression)] = CreateExpressionWriterThunk<MethodReferenceExpression>(this.WriteMethodReferenceExpression);
			writerTable[typeof(TypeReferenceExpression)] = CreateExpressionWriterThunk<TypeReferenceExpression>(this.WriteTypeReferenceExpression);
			writerTable[typeof(ObjectInitializerExpression)] = CreateExpressionWriterThunk<ObjectInitializerExpression>(this.WriteObjectInitializerExpression);
			writerTable[typeof(UnboundNewObjectExpression)] = CreateExpressionWriterThunk<UnboundNewObjectExpression>(this.WriteUnboundNewObjectExpression);
			writerTable[typeof(UnaryOperationExpression)] = CreateExpressionWriterThunk<UnaryOperationExpression>(this.WriteUnaryOperationExpression);
			writerTable[typeof(TypeCheckExpression)] = CreateExpressionWriterThunk<TypeCheckExpression>(this.WriteTypeCheckExpression);
			writerTable[typeof(ArgumentListExpression)] = CreateExpressionWriterThunk<ArgumentListExpression>(this.WriteArgumentListExpression);
			writerTable[typeof(LambdaExpression)] = CreateExpressionWriterThunk<LambdaExpression>(this.WriteLambdaExpression);
			writerTable[typeof(QueryExpression)] = CreateExpressionWriterThunk<QueryExpression>(this.WriteQueryExpression);
			writerTable[typeof(ProjectedQueryExpression)] = CreateExpressionWriterThunk<ProjectedQueryExpression>(this.WriteProjectedQueryExpression);
			writerTable[typeof(AnonymousMethodExpression)] = CreateExpressionWriterThunk<AnonymousMethodExpression>(this.WriteAnonymousMethodExpression);
			writerTable[typeof(SizeofExpression)] = CreateExpressionWriterThunk<SizeofExpression>(this.WriteSizeofExpression);
			writerTable[typeof(LocalAllocExpression)] = CreateExpressionWriterThunk<LocalAllocExpression>(this.WriteLocalAllocExpression);
			writerTable[typeof(IndirectExpression)] = CreateExpressionWriterThunk<IndirectExpression>(this.WriteIndirectExpression);
			writerTable[typeof(DereferenceExpression)] = CreateExpressionWriterThunk<DereferenceExpression>(this.WriteDereferenceExpression);

			this.ExpressionWriterTable = writerTable;
		}

		protected void WriteExpressionWithin(Expression inner, Expression outer)
		{
			if (inner == null) throw new ArgumentNullException("inner");
			//if (outer == null) throw new ArgumentNullException("outer");

			bool requiresParens =
				(inner is BinaryExpression)
				|| (inner is ConditionalExpression)
				|| (inner is CastExpression)
				|| (inner is QueryExpression)
				|| (inner is TypeCheckExpression)
				;

			if (requiresParens)
			{
				this.WriteText("(");
				this.WriteExpression(inner);
				this.WriteText(")");
			}
			else
			{
				this.WriteExpression(inner);
			}
		}
		public void WriteExpression(Expression expression)
		{
			//if (expression == null) throw new ArgumentNullException("expression");
			if (expression == null)
			{
				this.WriteText("?");
				return;
			}

			if (expression is ExceptionExpression)
			{
				return;
			}

			Action<Expression> writer = this.GetExpressionWriter(expression);
			if (writer == null) throw new ArgumentException("Cannot write expression");
			writer(expression);
		}

		private void WriteQueryClause(QueryClause clause)
		{
			DependentClause dependent = clause as DependentClause;
			if (dependent != null)
			{
				this.WriteQueryClause(dependent.Source);
				this.Writer.WriteLineBreak();
			}

			FromClause fromClause = clause as FromClause;
			if (fromClause != null)
			{
				this.WriteKeywordWithTrailingSpace("from");
				this.WriteIdentifier(fromClause.RangeVariable.Name);
				this.WriteText(" ");
				this.WriteKeywordWithTrailingSpace("in");
				this.WriteExpression(fromClause.Source);
			}
			else
			{
				WhereClause whereClause = clause as WhereClause;
				if (whereClause != null)
				{
					this.WriteKeywordWithTrailingSpace("where");
					this.WriteExpression(whereClause.Condition);
				}
				else
				{
					SelectManyClause selectManyClause = clause as SelectManyClause;
					if (selectManyClause != null)
					{
						this.WriteKeywordWithTrailingSpace("from");
						this.WriteIdentifier(selectManyClause.RangeVariable.Name);
						this.WriteText(" ");
						this.WriteKeywordWithTrailingSpace("in");
						this.WriteExpression(selectManyClause.EnumerableExpression);
					}
					else
					{
						OrderByClause orderbyClause = clause as OrderByClause;
						if (orderbyClause != null)
						{
							this.WriteKeywordWithTrailingSpace("orderby");
							this.WriteExpression(orderbyClause.SortExpression);
							if (orderbyClause.Direction == System.ComponentModel.ListSortDirection.Descending)
							{
								this.WriteText(" ");
								this.WriteKeywordText("descending");
							}
						}
						else
						{
							throw new ArgumentException("Unknown clause");
						}
					}
				}
			}
		}
		private void WriteQueryExpression(QueryExpression queryExpression)
		{
			this.Writer.Indent();
			this.Writer.EnsureNewLine();
			this.WriteQueryClause(queryExpression.Source);
			this.Writer.EnsureNewLine();
			this.WriteKeywordWithTrailingSpace("select");
			this.WriteIdentifier(queryExpression.RangeVariable.Name);
			this.Writer.Outdent();
			this.Writer.WriteLineBreak();
		}
		private void WriteProjectedQueryExpression(ProjectedQueryExpression queryExpression)
		{
			this.Writer.Indent();
			this.Writer.EnsureNewLine();
			this.WriteQueryClause(queryExpression.Source);
			this.Writer.EnsureNewLine();
			this.WriteKeywordWithTrailingSpace("select");
			this.WriteExpression(queryExpression.Projection);
			this.Writer.Outdent();
			this.Writer.WriteLineBreak();
		}
		private void WriteLambdaExpression(LambdaExpression lambdaExpression)
		{
			this.WriteText("(");

			bool first = true;
			foreach (var parameter in lambdaExpression.Parameters)
			{
				if (first) first = false;
				else this.WriteText(", ");
				this.WriteMemberReference(parameter.StaticVariableType);
				this.WriteText(" ");
				this.WriteIdentifier(parameter.Name);
			}
			this.WriteText(") => ");
			this.WriteExpressionWithin(lambdaExpression.Body, lambdaExpression);
		}
		private void WriteAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression)
		{
			this.WriteKeywordText("delegate");
			this.WriteText(" (");
			bool first = true;
			foreach (var parameter in anonymousMethodExpression.Parameters)
			{
				if (first) first = false;
				else this.WriteText(", ");
				this.WriteMemberReference(parameter.StaticVariableType);
				this.WriteText(" ");
				this.WriteIdentifier(parameter.Name);
			}
			this.WriteText(")");
			this.OpenBlock();
			this.WriteStatementBlock(anonymousMethodExpression.Body);
			this.CloseBlock();
		}
		private void WriteArgumentListExpression(ArgumentListExpression argumentListExpression)
		{
			this.WriteKeywordText("__arglist");
		}
		private void WriteSizeofExpression(SizeofExpression sizeofExpression)
		{
			this.WriteKeywordText("sizeof");
			this.WriteText("(");
			this.WriteTypeReference(((BoundTypeReference)sizeofExpression.Type).Type);
			this.WriteText(")");
		}
		private void WriteIndirectExpression(IndirectExpression indirect)
		{
			this.Writer.WriteText("&", CodeTextType.Operator);
			this.WriteExpression(indirect.Expression);
		}
		private void WriteDereferenceExpression(DereferenceExpression dereference)
		{
			this.Writer.WriteText("*", CodeTextType.Operator);
			this.WriteExpressionWithin(dereference.Expression, dereference);
		}
		private void WriteLocalAllocExpression(LocalAllocExpression locallocExpression)
		{
			this.WriteKeywordText("localloc");
			this.WriteText("(");
			this.WriteExpression(locallocExpression.Size);
			this.WriteText(")");
		}
		private void WriteTypeCheckExpression(TypeCheckExpression typeCheckExpression)
		{
			this.WriteExpressionWithin(typeCheckExpression.Operand, typeCheckExpression);
			this.WriteText(" ");
			this.WriteKeywordWithTrailingSpace("is");
			this.WriteMemberReference(typeCheckExpression.Type);
		}
		private void WriteUnaryOperationExpression(UnaryOperationExpression unaryOperationExpression)
		{
			if (unaryOperationExpression.Operator.Valence == ExpressionOperatorValence.UnaryPrefix)
			{
				this.WriteOperator(unaryOperationExpression.Operator, unaryOperationExpression.OperatorMethod);
				this.WriteExpressionWithin(unaryOperationExpression.Operand, unaryOperationExpression);
			}
			else
			{
				this.WriteExpressionWithin(unaryOperationExpression.Operand, unaryOperationExpression);
				this.WriteOperator(unaryOperationExpression.Operator, unaryOperationExpression.OperatorMethod);
			}
		}
		private void WriteObjectInitializerExpression(ObjectInitializerExpression objectInitializerExpression)
		{
			this.WriteExpression(objectInitializerExpression.NewObjectExpression);

			this.OpenInlineBlock();
			bool first = true;
			foreach (var initializer in objectInitializerExpression.Initializers)
			{
				if (first) first = false;
				else this.WriteText(", ");

				this.WriteIdentifier(MemberViewInfo.GetMemberName(initializer.Member));
				this.WriteText(" = ");
				this.WriteExpression(initializer.Value);
			}

			this.CloseInlineBlock();
		}
		private void WriteTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression)
		{
			this.WriteKeywordText("typeof");
			this.WriteText("(");
			this.WriteMemberReference(typeReferenceExpression.Type);
			this.WriteText(")");
		}
		private void WriteMethodReferenceExpression(MethodReferenceExpression methodReferenceExpression)
		{
			MethodInfo method = methodReferenceExpression.Method;
			if (method.IsStatic)
			{
				this.WriteMemberReference(method.ReflectedType);
			}
			else
			{
				this.WriteExpressionWithin(methodReferenceExpression.Instance, methodReferenceExpression);
			}
			this.WriteText(".");
			this.Writer.WriteMemberReference(method, MemberViewInfo.GetMemberName(method));
		}
		private void WriteIndexerExpression(IndexerExpression indexerExpression)
		{
			this.WriteExpressionWithin(indexerExpression.InstanceExpression, indexerExpression);
			this.WriteText("[");
			this.WriteExpressionList(indexerExpression.Arguments);
			this.WriteText("]");
		}
		private void WriteNewImplicitArrayExpression(NewImplicitArrayExpression newImplicitArrayExpression)
		{
			this.WriteKeywordWithTrailingSpace("new");
			this.WriteMemberReference(newImplicitArrayExpression.ElementType);
			this.WriteText("[] { ");
			this.WriteExpressionList(newImplicitArrayExpression.Arguments);
			this.WriteText(" }");
		}
		private void WriteAssignmentExpression(AssignmentExpression assignmentExpression)
		{
			this.WriteExpressionWithin(assignmentExpression.Left, assignmentExpression);
			this.WriteText(" = ");
			this.WriteExpressionWithin(assignmentExpression.Right, assignmentExpression);
		}
		private void WriteConditionalExpression(ConditionalExpression conditionalExpression)
		{
			this.WriteExpressionWithin(conditionalExpression.Condition, conditionalExpression);
			this.WriteText(" ? ");
			this.WriteExpressionWithin(conditionalExpression.TrueValue, conditionalExpression);
			this.WriteText(" : ");
			this.WriteExpressionWithin(conditionalExpression.FalseValue, conditionalExpression);
		}
		private void WriteNewArrayExpression(NewArrayExpression newArray)
		{
			this.WriteKeywordWithTrailingSpace("new");
			this.WriteMemberReference(newArray.ElementType);
			this.WriteText("[");

			bool first = true;
			foreach (var dimension in newArray.Dimensions)
			{
				if (first) first = false;
				else this.WriteText(", ");

				this.WriteExpression(dimension);
			}
			this.WriteText("]");
		}
		private void WriteOperator(ExpressionOperator op, MethodInfo operatorMethod = null)
		{
			string symbol = op.Symbol;
			if (operatorMethod != null)
				this.Writer.WriteMemberReference(operatorMethod, symbol, CodeTextType.Operator);
			else
				this.WriteText(symbol);
		}
		private void WriteArgumentList(ParameterInfo[] parameters, Expression[] arguments)
		{
			if (arguments == null) throw new ArgumentNullException("arguments");

			this.WriteText("(");

			bool first = true;
			for (int i = 0; i < arguments.Length; i++)
			{
				if (i > 0) this.WriteText(", ");

				Expression argumentExpression = arguments[i];
				if (parameters != null)
				{
					ParameterInfo param = parameters[i];
					if (param.ParameterType.IsByRef)
					{
						string keyword = param.IsIn ? "ref" : "out";
						this.WriteKeywordText(keyword);
						this.WriteText(" ");

						IndirectExpression indirect = argumentExpression as IndirectExpression;
						if (indirect != null) argumentExpression = indirect.Expression;
					}
				}
				this.WriteExpression(argumentExpression);
			}

			this.WriteText(")");
		}
		private void WriteExpressionList(IEnumerable<Expression> arguments)
		{
			if (arguments == null) throw new ArgumentNullException("arguments");

			bool first = true;
			foreach (var argument in arguments)
			{
				if (first) first = false;
				else
					this.WriteText(", ");

				this.WriteExpression(argument);
			}
		}
		private void WriteInstanceReferenceExpression(InstanceReferenceExpression instanceReferenceExpression)
		{
			this.WriteKeywordText("this");
		}
		private void WriteVariableReferenceExpression(VariableReferenceExpression variableReferenceExpression)
		{
			this.WriteIdentifier(variableReferenceExpression.VariableName);
		}
		private void WriteBinaryOperationExpression(BinaryOperationExpression binaryOperationExpression)
		{
			this.WriteExpressionWithin(binaryOperationExpression.Left, binaryOperationExpression);
			this.WriteText(" ");
			this.WriteOperator(binaryOperationExpression.Operator, binaryOperationExpression.OperatorMethod);
			this.WriteText(" ");
			this.WriteExpressionWithin(binaryOperationExpression.Right, binaryOperationExpression);
		}
		private void WriteBoundConstructorExpression(BoundConstructorExpression boundConstructorExpression)
		{
			this.WriteKeywordWithTrailingSpace("new");
			this.WriteMemberReference(boundConstructorExpression.Constructor);
			this.WriteText("(");
			this.WriteExpressionList(boundConstructorExpression.Arguments);
			this.WriteText(")");
		}
		private void WriteUnboundNewObjectExpression(UnboundNewObjectExpression unboundNewObjectExpression)
		{
			this.WriteKeywordWithTrailingSpace("new");
			this.WriteMemberReference(((BoundTypeReference)unboundNewObjectExpression.ObjectType).Type);
			this.WriteText("(");
			this.WriteExpressionList(unboundNewObjectExpression.Arguments);
			this.WriteText(")");
		}
		private void WriteBoundFunctionCallExpression(BoundFunctionCallExpression functionCallExpression)
		{
			BoundMethodCallFunction methodCall = (BoundMethodCallFunction)functionCallExpression.Function;
			MethodInfo method = methodCall.Method;

			if (method.IsStatic)
			{
				if (method.ReflectedType != null)
				{
					this.WriteMemberReference(method.ReflectedType);
					this.WriteText(".");
				}
			}
			else
			{
				this.WriteExpressionWithin(methodCall.Instance, functionCallExpression);
				this.WriteText(".");
			}

			this.WriteMemberReference(method);
			this.WriteArgumentList(method.GetParameters(), functionCallExpression.Arguments.ToArray());
		}
		private void WriteIndirectCallExpression(IndirectCallExpression indirectCallExpression)
		{
			IEnumerable<Expression> argumentList;

			argumentList = indirectCallExpression.Arguments;

			this.WriteExpressionWithin(indirectCallExpression.Target, indirectCallExpression);
			this.WriteArgumentList(null, argumentList.ToArray());
		}
		private void WriteCastExpression(CastExpression castExpression)
		{
			if (castExpression.IsSoft)
			{
				this.WriteExpressionWithin(castExpression.Operand, castExpression);
				this.WriteText(" ");
				this.WriteKeywordText("as");
				this.WriteText(" ");
				this.WriteMemberReference(castExpression.TargetType);
			}
			else
			{
				this.WriteText("(");
				this.WriteMemberReference(castExpression.TargetType);
				this.WriteText(")");
				this.WriteExpressionWithin(castExpression.Operand, castExpression);
			}
		}
		private void WriteConstantExpression(ConstantExpression constantExpression)
		{
			object value = constantExpression.Value;
			WriteValue(value);
		}

		#region Values
		private void WriteValue(object value)
		{
			if (value == null)
			{
				this.WriteNullValue();
			}
			else
			{
				if (value is string)
					this.WriteStringValue((string)value);
				else if (value is char)
					this.WriteCharValue((char)value);
				else if (value is byte)
					this.WriteByteValue((byte)value);
				else if (value is sbyte)
					this.WriteSByteValue((sbyte)value);
				else if (value is short)
					this.WriteInt16Value((short)value);
				else if (value is ushort)
					this.WriteUInt16Value((ushort)value);
				else if (value is int)
					this.WriteInt32Value((int)value);
				else if (value is uint)
					this.WriteUInt32Value((uint)value);
				else if (value is long)
					this.WriteInt64Value((long)value);
				else if (value is ulong)
					this.WriteUInt64Value((ulong)value);
				else if (value is decimal)
					this.WriteDecimalValue((decimal)value);
				else if (value is float)
					this.WriteFloatValue((float)value);
				else if (value is bool)
					this.WriteBooleanValue((bool)value);

				// UNDONE: Enum values represented as field references
				// This fixes the problem with enums defined outside system assemblies
				//else if (value is Enum)
				//{
				//    Type enumType = value.GetType();
				//    string enumName = Enum.GetName(enumType, value);
				//    if (enumName != null)
				//    {
				//        this.WriteFieldReferenceExpression(new FieldReferenceExpression(null, (enumType.GetField(enumName))));
				//    }
				//    else
				//    {
				//        int enumValue = (int)value;

				//        FlagsAttribute flags = (FlagsAttribute)Attribute.GetCustomAttribute(enumType, typeof(FlagsAttribute));
				//        if ((flags == null) || (enumValue == 0))
				//        {
				//            this.WriteCastExpression(new CastExpression(ConstantExpression.FromValue((int)value), enumType));
				//        }
				//        else
				//        {
				//            Array enumValues = Enum.GetValues(enumType);
				//            Array.Reverse(enumValues);

				//            bool first = true;
				//            foreach (int item in enumValues)
				//            {
				//                if ((enumValue & item) == item)
				//                {
				//                    enumValue &= ~item;
				//                    string itemName = Enum.GetName(enumType, item);
				//                    if (first) first = false;
				//                    else this.WriteText(" | ");

				//                    this.WriteFieldReferenceExpression(new FieldReferenceExpression(null, (enumType.GetField(itemName))));
				//                }
				//            }
				//        }
				//    }
				//}
				else
				{
					this.WriteText(value.ToString());
				}
			}
		}

		protected virtual void WriteNullValue()
		{
			this.WriteKeywordText("null");
		}

		protected virtual void WriteBooleanValue(bool value)
		{
			this.WriteKeywordText(value ? "true" : "false");
		}

		protected virtual void WriteSByteValue(sbyte value)
		{
			this.WriteSignedNumeric(value);
		}

		protected virtual void WriteByteValue(byte value)
		{
			this.WriteUnsignedNumeric(value);
		}

		protected virtual void WriteInt16Value(short value)
		{
			this.WriteSignedNumeric(value);
		}

		protected virtual void WriteUInt16Value(ushort value)
		{
			this.WriteUnsignedNumeric(value);
		}

		protected virtual void WriteInt32Value(int value)
		{
			this.WriteSignedNumeric(value);
		}

		protected virtual void WriteUInt32Value(uint value)
		{
			this.WriteUnsignedNumeric(value);
		}

		protected virtual void WriteInt64Value(long value)
		{
			this.WriteSignedNumeric(value);
			this.WriteText("L");
		}

		protected virtual void WriteUInt64Value(ulong value)
		{
			this.WriteUnsignedNumeric(value);
			this.WriteText("L");
		}

		protected virtual void WriteUnsignedNumeric(ulong value)
		{
			this.Writer.WriteText(value.ToString(), CodeTextType.Literal);
		}

		protected virtual void WriteSignedNumeric(long value)
		{
			this.Writer.WriteText(value.ToString(), CodeTextType.Literal);
		}

		protected virtual void WriteFloatValue(float value)
		{
			this.Writer.WriteText(value.ToString(), CodeTextType.Literal);
			this.WriteText("F");
		}

		protected virtual void WriteDecimalValue(decimal value)
		{
			this.Writer.WriteText(value.ToString(), CodeTextType.Literal);
			this.WriteText("M");
		}

		protected virtual void WriteCharValue(char value)
		{
			string text = string.Format("'{0}'", EscapeString(value.ToString()));
			this.Writer.WriteText(text, CodeTextType.Literal);
		}

		protected virtual void WriteStringValue(string value)
		{
			string text = string.Format("\"{0}\"", EscapeString(value));
			this.Writer.WriteText(text, CodeTextType.Literal);
		}
		#endregion

		private void WriteFieldReferenceExpression(FieldReferenceExpression fieldReferenceExpression)
		{
			bool includeDot = false;
			if (fieldReferenceExpression.Field.IsStatic)
			{
				if (fieldReferenceExpression.Field.ReflectedType != null)
				{
					includeDot = true;
					this.WriteMemberReference(fieldReferenceExpression.Field.ReflectedType);
				}
			}
			else
			{
				includeDot = true;
				this.WriteExpressionWithin(fieldReferenceExpression.InstanceExpression, fieldReferenceExpression);
			}

			if (includeDot) this.WriteText(".");
			this.WriteMemberReference(fieldReferenceExpression.Member);
		}
		private void WritePropertyReferenceExpression(PropertyReferenceExpression propertyReferenceExpression)
		{
			Expression baseExpression = propertyReferenceExpression.InstanceExpression;
			if (baseExpression == null)
			{
				this.WriteMemberReference(propertyReferenceExpression.Property.ReflectedType);
			}
			else
			{
				this.WriteExpressionWithin(propertyReferenceExpression.InstanceExpression, propertyReferenceExpression);
			}

			this.WriteText(".");
			this.WriteMemberReference(propertyReferenceExpression.Member);
		}
		#endregion
	}
}
